#include <SFML/Window.hpp>
#include <iostream>
#include <random>

#include "draw.h"
#include "phitron.h"
#include "physics.h"

#define MOVEMENT_MAGNITUDE 5.0
#define TURN_MAGNITUDE 0.01

extern bool toDraw;

Vector3 forward(0.0, 0.0, -1.0);
Matrix3 cameraRotation(1, 0, 0, 0, 1, 0, 0, 0, 1);
Vector3 cameraPosition(0.0, 0.0, 0.0);

bool keyU = false;
bool keyD = false;
bool keyL = false;
bool keyR = false;
bool keyQ = false;
bool keyE = false;

std::default_random_engine generator(1587);
std::uniform_real_distribution<double> forces(-2, 2);
std::uniform_real_distribution<double> quanta(-8, 8);
std::uniform_real_distribution<double> inertias(1, 1);
std::uniform_real_distribution<double> positions(-64, 64);
std::uniform_real_distribution<double> velocities(-0.5, 0.5);
phitron::Laws<TOTAL_FORCES> laws;

void update_projection() {
    glLoadIdentity();

    //Matrix3 single(1, 0, 0, 0, 1, 0, 0, 0, 1);
    draw::m3d(cameraRotation);
    draw::t3d(-cameraPosition);
}

int foo = 0;
phitron::Group<TOTAL_FORCES> group;

void update_init() {
    //Ideal
    //laws.friction = 5;
    laws.friction = 0.0;
    for (int i = 0; i < TOTAL_FORCES; i++)
        for (int j = 0; j < TOTAL_FORCES; j++) {
            laws.forces[i][j].attraction = forces(generator);
            laws.forces[i][j].cross = forces(generator);
        }
}

void update() {
    foo++;

    for (int i = 0; i < 1; i++) {
        group.forces(laws);
        group.update(0.01);
        for (Link<phitron::Object<TOTAL_FORCES>>* link = group.objects; link; link = link->next) {
            if (link->object.position.x > SPACE_BOUNDS) {
                link->object.position.x += SPACE_BOUNDS - link->object.position.x;
                link->object.velocity.x *= -1;
            }
            if (link->object.position.x < -SPACE_BOUNDS) {
                link->object.position.x += -SPACE_BOUNDS - link->object.position.x;
                link->object.velocity.x *= -1;
            }
            if (link->object.position.y > SPACE_BOUNDS) {
                link->object.position.y += SPACE_BOUNDS - link->object.position.y;
                link->object.velocity.y *= -1;
            }
            if (link->object.position.y < -SPACE_BOUNDS) {
                link->object.position.y += -SPACE_BOUNDS - link->object.position.y;
                link->object.velocity.y *= -1;
            }
            if (link->object.position.z > SPACE_BOUNDS) {
                link->object.position.z += SPACE_BOUNDS - link->object.position.z;
                link->object.velocity.z *= -1;
            }
            if (link->object.position.z < -SPACE_BOUNDS) {
                link->object.position.z += -SPACE_BOUNDS - link->object.position.z;
                link->object.velocity.z *= -1;
            }
        }
    }

    Vector3 streight = cameraRotation * forward * MOVEMENT_MAGNITUDE;

    if (keyU)
        cameraPosition = cameraPosition + streight;
    if (keyD)
        cameraPosition = cameraPosition - streight;

    Vector3 strafe = cameraRotation * Matrix3(0.0, M_PI/2) * forward * MOVEMENT_MAGNITUDE;

    if (keyL)
        cameraPosition = cameraPosition + strafe;
    if (keyR)
        cameraPosition = cameraPosition - strafe;

    Vector3 rise = cameraRotation * Matrix3(M_PI/2, 0.0) * forward * MOVEMENT_MAGNITUDE;

    if (keyE)
        cameraPosition = cameraPosition + rise;
    if (keyQ)
        cameraPosition = cameraPosition - rise;

    update_projection();
}

void handle_key(sf::Event::KeyEvent key, bool pressed) {
    /*
    int choice1;
    int choice2;
    int choice3;
    int choice4;
    int choice5;
    int choice6;
    int value1;
    int value2;
    int value3;
    int value4;
    int value5;
    int value6;*/
    if (pressed)
        switch (key.code) {
        case sf::Keyboard::W:
            keyU = true;
            break;

        case sf::Keyboard::A:
            keyL = true;
            break;

        case sf::Keyboard::S:
            keyD = true;
            break;

        case sf::Keyboard::D:
            keyR = true;
            break;

        case sf::Keyboard::Q:
            keyQ = true;
            break;

        case sf::Keyboard::E:
            keyE = true;
            break;

        case sf::Keyboard::O:
            for (int i = 0; i < (key.control ? 32 : 1); i++) {
                /*
                choice1 = rand()%2 ? 1 : -1;
                choice2 = rand()%2 ? 1 : -1;
                choice3 = rand()%2 ? 1 : -1;
                choice4 = rand()%2 ? 1 : -1;
                choice5 = rand()%2 ? 1 : -1;
                choice6 = rand()%2 ? 1 : -1;
                value1 = rand()%33;
                value2 = rand()%33;
                value3 = rand()%33;
                value4 = rand()%33;
                value5 = rand()%33;
                value6 = rand()%33;*/
                Link<phitron::Object<TOTAL_FORCES>>* link = new Link<phitron::Object<TOTAL_FORCES>>();
                double quantae[TOTAL_FORCES];
                for (int i = 0; i < TOTAL_FORCES; i++)
                    quantae[i] = rand()&1 ? quanta(generator) : 0;
                link->object = phitron::Object<TOTAL_FORCES>(inertias(generator),
                    Vector3(positions(generator), positions(generator), positions(generator)),
                    Vector3(velocities(generator), velocities(generator), velocities(generator)),
                    quantae
                    );
                group.add(link);
            }
            break;

        case sf::Keyboard::T:
            toDraw = !toDraw;
            break;

        default:
            break;
        }
    else
        switch (key.code) {
        case sf::Keyboard::W:
            keyU = false;
            break;

        case sf::Keyboard::A:
            keyL = false;
            break;

        case sf::Keyboard::S:
            keyD = false;
            break;

        case sf::Keyboard::D:
            keyR = false;
            break;

        case sf::Keyboard::Q:
            keyQ = false;
            break;

        case sf::Keyboard::E:
            keyE = false;
            break;

        default:
            break;
        }
}

void handle_mouse(sf::Event::MouseMoveEvent event) {
    cameraRotation *= Matrix3(-(event.y - 500) * TURN_MAGNITUDE, -(event.x - 500) * TURN_MAGNITUDE);
}
